package pt.inescid.components.policyengine.heimdall.database;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.Vector;

import pt.inescid.components.policyengine.heimdall.base.EventField;
import pt.inescid.components.policyengine.heimdall.base.EventFieldCode;
import pt.inescid.components.policyengine.heimdall.base.EventObj;
import pt.inescid.components.policyengine.heimdall.base.EventRelevance;
import pt.inescid.components.policyengine.heimdall.base.User;
import pt.inescid.components.policyengine.heimdall.commons.DBManager;
import pt.inescid.components.policyengine.heimdall.commons.Log;
import pt.inescid.components.policyengine.heimdall.commons.Utilities;
import pt.inescid.components.policyengine.heimdall.pap.base.CesDefinition;
import pt.inescid.components.policyengine.heimdall.pap.base.ModelDefinition;
import pt.inescid.components.policyengine.heimdall.pap.base.RuleDefinition;

public class EventObjectDAO {
	


	/* ===========================================================
	 ==============================================================
	 =============                    =============================
	 ============= getEvent =====================
	 =============                    =============================
	 ==============================================================
	 =========================================================== */
	public static EventObj getEvent(
			User currentUser, ModelDefinition currentModelDef,
			int recordId) {
		
		ResultSet rs;
		EventObj thisEvent;
		
		String eventsTableName = "event_U" +
				Utilities.getFormattedNumber(
						new Long(currentUser.getId()).intValue(), 4) +
						"_" + currentModelDef.getTag();

		String query = "SELECT * FROM " + eventsTableName + 
				" WHERE id=" + recordId + ";";
		
		try {
			rs = DBManager.executeQuery(query);
			
			if (rs.first()) {
				thisEvent = createRecord(rs, currentModelDef);
			}
			else {
				thisEvent = null;
			};
			
		}
		catch (SQLException e) {
			Log.debug(Log.ERROR, "Erro SQL: " + e + 
					" - query = " + query, "EventObjectDAO.getRecord");
			thisEvent = null;
		}
		
		return (thisEvent);
	}; // getRecord
	
	
	
	
	/* ===========================================================
	 ==============================================================
	 =============                    =============================
	 ============= getAllRecordsAfter =====================
	 =============                    =============================
	 ==============================================================
	 =========================================================== */
//	public static Vector getAllEventsAfter(User currentUser, 
//			String modelDefTag, 
//			EventObj ce,
//			String cesName,
//			long lastEventId) {
//		
//		Log.debug(Log.INFO, "Arrived.", "EventObjectDAO.getAllRecordsAfter");
//
//		ResultSet rs;
//		Vector allEvents = new Vector();
//		
//		ModelDefinition currentModelDef = 
//			currentUser.getModelByTag(modelDefTag);
//		
//		String eventsTableName = "event_U" +
//				Utilities.getFormattedNumber(
//						new Long(currentUser.getId()).intValue(), 4) +
//						"_" + modelDefTag;
//
//		String query = "SELECT * FROM " + eventsTableName + 
//				" WHERE " +
//				"SUBSTRING(tag," + 
//				(currentModelDef.getRelevantCesIndex(cesName)+1) + ",1)='1' and " +
////				"TO_DAYS(NOW()) - TO_DAYS(created) <= 30 and " +
//				"id>" + 
//				lastEventId;
//		
//		CesDefinition currentCes = currentModelDef.getCes(cesName);
//		RuleDefinition cesGlobalRestriction = currentCes.getGlobalRestriction();
//		String cesSqlRestriction = cesGlobalRestriction.getSqlRestriction(
//				currentUser, ce);
//		
//		if (!cesSqlRestriction.equals("")) {
//			query += " AND " +  cesSqlRestriction; 
//		}
//		
//		query += ";";
//
//		
//		try {
//			rs = DBManager.executeQuery(query);
//			
//			if (rs.first()) {
//				EventObject currentEventRecord = null;
//				do {
//					currentEventRecord = createRecord(rs, currentModelDef);
//					allEvents.add(new EventObj(currentEventRecord.getId(),
//							currentEventRecord));
//				} while (rs.next());
//			}
//			
//		}
//		catch (SQLException e) {
//			Log.debug(Log.ERROR, "Erro SQL: " + e + 
//					" - query = " + query, "EventObjectDAO.getAllRecordsAfter");
//			allEvents = null;
//		}
//		
//		return (allEvents);
//	}; // getAllRecordsAfter
//	
	
	/* ===========================================================
	 ==============================================================
	 =============                    =============================
	 ============= getAllRecords =====================
	 =============                    =============================
	 ==============================================================
	 =========================================================== */
	public static Vector getAllRecords(User currentUser, String modelTag) {
		
		Log.debug(Log.INFO, "Arrived.", "EventObjectDAO.getAllRecords");

		ResultSet rs;
		Vector allEvents = new Vector();
		
		ModelDefinition currentModelDef = 
			currentUser.getModelByTag(modelTag);
		
		String eventsTableName = "event_U" +
				Utilities.getFormattedNumber(
						new Long(currentUser.getId()).intValue(), 4) +
						"_" + modelTag;

		String query = "SELECT * FROM " + eventsTableName + " ORDER BY id;";

		
		try {
			rs = DBManager.executeQuery(query);
			
			if (rs.first()) {
				EventObj currentEventRecord = null;
				do {
					currentEventRecord = 
						createRecord(rs, currentModelDef);
					allEvents.add(currentEventRecord);
				} while (rs.next());
			}
			
		}
		catch (SQLException e) {
			Log.debug(Log.ERROR, "Erro SQL: " + e + 
					" - query = " + query, "EventObjectDAO.getAllRecords");
			allEvents = null;
		}
		
		return (allEvents);
	}; // getAllRecords
	
	
	/* ===========================================================
	 ==============================================================
	 =============                    =============================
	 ============= getAllRecordsAfter =====================
	 =============                    =============================
	 ==============================================================
	 =========================================================== */
	public static Vector getAllEventsAfter(User currentUser, 
			String modelDefTag, 
			String cesSqlRestriction,
			String cesName,
			long lastEventId) {
	
		return (getAllEventsAfter(
				currentUser,
				modelDefTag,
				cesSqlRestriction,
				cesName,
				lastEventId,
				false));
	} // getAllEventsAfter
	
	
	
	public static Vector getAllEventsAfter(User currentUser, 
			String modelDefTag, 
			String cesSqlRestriction,
			String cesName,
			long lastEventId,
			boolean includeTags) {
		
		ResultSet rs;
		Vector allEvents = new Vector();
		
		ModelDefinition currentModelDef = 
			currentUser.getModelByTag(modelDefTag);
		
		String eventsTableName = "event_U" +
				Utilities.getFormattedNumber(
						new Long(currentUser.getId()).intValue(), 4) +
						"_" + modelDefTag;

		int relevantCesIndex = currentModelDef.getRelevantCesIndex(cesName);
		
		String query = "SELECT * FROM " + eventsTableName + 
				" WHERE " +
				"SUBSTRING(tag," + (relevantCesIndex+1) + 
					",1)='1' and " +
//				"TO_DAYS(NOW()) - TO_DAYS(created) <= 30 and " +
				"id>" + 
				lastEventId;
		
		if (cesSqlRestriction != null && !cesSqlRestriction.equals("")) {
			query += " AND " +  cesSqlRestriction; 
		}
		
		query += ";";

		
		try {
			rs = DBManager.executeQuery(query);
			
			if (rs.first()) {
				EventObj currentEventRecord = null;
				do {
					currentEventRecord = createRecord(rs, 
							currentModelDef, includeTags);
					allEvents.add(currentEventRecord);
				} while (rs.next());
			}
			
		}
		catch (SQLException e) {
			Log.debug(Log.ERROR, "Erro SQL: " + e + 
					" - query = " + query, "EventObjectDAO.getAllRecordsAfter");
			allEvents = null;
		}
		
		return (allEvents);
	}; // getAllRecordsAfter
	
	
	
	
	
	
	
	/* ===========================================================
	 ==============================================================
	 =============                    =============================
	 =============      createEvent   =============================
	 =============                    =============================
	 ==============================================================
	 =========================================================== */
	
//	public static EventObj createEvent(ResultSet rs,
//			ModelDefinition currentModelDef)
//	throws SQLException {
//		EventObject newRecord = null;
//		EventObj newEvent = null;
//		
//		Vector allFields = null;
//		long recordId = 0;
//		try {
//			recordId = rs.getLong("id");
//			byte[] buf = rs.getBytes("info");
//			
//			// Events fields
//			if (buf != null) {
//				ObjectInputStream objectIn = new ObjectInputStream(
//						new ByteArrayInputStream(buf));
//				allFields = (Vector) objectIn.readObject();
//			}
//			newRecord = new EventObject(recordId, allFields);
//		}
//		catch (Exception e) {
//			Log.debug(Log.ERROR, "Erro SQL: " + e, "EventObjectDAO.createRecord");
//			newRecord = null;
//		}
//		
//		newEvent = new EventObj(recordId, newRecord);
//		return (newEvent);
//	}; // createEvent
//	
//	
//	
	
	/* ===========================================================
	 ==============================================================
	 =============                    =============================
	 =============      createRecord   =============================
	 =============                    =============================
	 ==============================================================
	 =========================================================== */
	public static EventObj createRecord(ResultSet rs,
			ModelDefinition currentModelDef) throws SQLException {

		return (createRecord(
				rs, currentModelDef, false));
	} // createRecord
	
	
	
	public static EventObj createRecord(ResultSet rs,
			ModelDefinition currentModelDef,
			boolean includeTags) throws SQLException {
		
		EventObj newRecord = null;
		
		Vector allFields = null;
		long recordId = 0;
		try {
			recordId = rs.getLong("id");
			byte[] buf = rs.getBytes("info");
			if (buf != null) {
				ObjectInputStream objectIn = new ObjectInputStream(
						new ByteArrayInputStream(buf));
				allFields = (Vector) objectIn.readObject();
			}
			
			if (includeTags) {
				allFields.add(new EventField(
						EventFieldCode.TAGS_FIELD_ID,
						rs.getString("tag")));
			}
			newRecord = new EventObj(recordId, allFields);
			
		}
		catch (Exception e) {
			Log.debug(Log.ERROR, "Erro SQL: " + e, "EventObjectDAO.createRecord");
			newRecord = null;
		}
		
		// fill parameters
		EventRelevance currentParameter = null;
		Vector allRelevantCesParameters = 
			currentModelDef.getRelevantCesParameters();
		for (int i = 0; i < allRelevantCesParameters.size(); i++) {
			currentParameter = (EventRelevance) 
					allRelevantCesParameters.elementAt(i);
			
			switch (currentParameter.getFieldType()) {
			case RuleDefinition.STRING_VALUE:
				newRecord.addField(new EventField(
						currentModelDef.getFieldId(
								currentParameter.getFieldName()),
						rs.getString(
								currentParameter.
								getEventFieldNameForSQL())));
				break;
			case RuleDefinition.INT_VALUE:
				newRecord.addField(new EventField(
						currentModelDef.getFieldId(
								currentParameter.getFieldName()),
						new Integer(rs.getInt(
								currentParameter.
								getEventFieldNameForSQL()))));
				break;
			case RuleDefinition.LONG_VALUE:
				newRecord.addField(new EventField(
						currentModelDef.getFieldId(
								currentParameter.getFieldName()),
						new Long(rs.getLong(
								currentParameter.
								getEventFieldNameForSQL()))));
				break;
			case RuleDefinition.TIMESTAMP_VALUE:
				newRecord.addField(new EventField(
						currentModelDef.getFieldId(
								currentParameter.getFieldName()),
						new Long(rs.getTimestamp(
								currentParameter.
								getEventFieldNameForSQL()).getTime())));
				break;

			default:
				break;
			}
		}

		return (newRecord);
	}; // createRecord
	
	
	
	
	
	/* ===========================================================
	 ==============================================================
	 =============                    =============================
	 =============      storeIfRelevant =========================
	 =============                    =============================
	 ==============================================================
	 =========================================================== */
	public static boolean storeIfRelevant(EventObj newRecord, 
			User currentUser, ModelDefinition currentModelDef) {
		
		// check if the event is relevant for any Ces and
		// set the event tag
		String eventTag = "";
		Vector allRelevantCes = currentModelDef.getAllRelevantCes();
		CesDefinition currentRelevantCes = null;
		for (int i = 0; i < allRelevantCes.size(); i++) {
			currentRelevantCes = (CesDefinition) allRelevantCes.elementAt(i);
			if (currentRelevantCes.isRelevant(newRecord)) {
				eventTag += "1";
			}
			else {
				eventTag += "0";
			}
		}
		
		// if the event is not relevant, return
		if (!eventTag.contains("1")) {
			return (false);
		}
		
		String insertQuery = null;
		
		try {
			ByteArrayOutputStream infoAsBytes = new ByteArrayOutputStream();
			ObjectOutputStream infoAsObject = new ObjectOutputStream(
					infoAsBytes);
			infoAsObject.writeObject(newRecord.getInfoFields(currentModelDef));
			
			String eventsTableName = "event_U" +
					Utilities.getFormattedNumber(
							new Long(currentUser.getId()).intValue(), 4) +
							"_" + currentModelDef.getTag();

			String query = "INSERT INTO " + eventsTableName + 
					"(info";

			Vector allRelevantCesParameters = 
				currentModelDef.getRelevantCesParameters();
			
			EventRelevance currentParameter = null;
			for (int i = 0; i < allRelevantCesParameters.size(); i++) {
				currentParameter = (EventRelevance) 
						allRelevantCesParameters.elementAt(i);
				query += "," + 
				currentParameter.getEventFieldNameForSQL();
			}
			query += ",tag) VALUES (?";
			
			for (int i = 0; i < allRelevantCesParameters.size(); i++) {
				query += ",?"; 
			}
			query += ",?);"; 
			
			PreparedStatement ps;
			ps = DBManager.getConnection().prepareStatement(query);
			
			// set all the evaluation fields (not including the parameters)
			ps.setBytes(1,infoAsBytes.toByteArray());

			// set the parameters
			if (allRelevantCesParameters != null) {
				for (int i = 0; i < allRelevantCesParameters.size(); i++) {
					currentParameter = (EventRelevance) 
							allRelevantCesParameters.elementAt(i);
					switch (currentParameter.getFieldType()) {
					case RuleDefinition.STRING_VALUE:
						ps.setString((i+2), 
								(String) (newRecord.getField(
										currentModelDef.getFieldId(
												currentParameter.getFieldName()))).
								getValue());
						break;
					case RuleDefinition.LONG_VALUE:
						ps.setLong((i+2), 
								((Long) (newRecord.getField(
										currentModelDef.getFieldId(
												currentParameter.getFieldName()))).
								getValue()).longValue());
						break;
					case RuleDefinition.TIMESTAMP_VALUE:
						ps.setTimestamp((i+2), 
								new Timestamp(
										((Long) (newRecord.getField(
												currentModelDef.getFieldId(
														currentParameter.getFieldName()))).
										getValue()).longValue()));
						break;
						
					default:
						break;
					}
				}
			}
			
			ps.setString(1+allRelevantCesParameters.size()+1,eventTag);

			ps.execute();
			ps.close();
		}
		catch (Exception e) {
			Log.debug(Log.ERROR, "Erro SQL: " + e + 
					" - query = " + insertQuery, "EventObjectDAO.insertRecord");
		}
		
		return (true);
	} // storeIfRelevant
	
	
	
	
	
	
	/* ===========================================================
	 ==============================================================
	 =============                    =============================
	 =============      purge    =========================
	 =============                    =============================
	 ==============================================================
	 =========================================================== */
	public static void purge(User currentUser, 
			ModelDefinition currentModelDef, 
			CesDefinition currentCes) {
		
		
		int cesTagIndex = currentModelDef.getRelevantCesIndex(
				currentCes.getName());
		
		Vector allEventObjects = getAllEventsAfter(
				currentUser,
				currentModelDef.getTag(), 
				null,
				currentCes.getName(),
				Utilities.UNDEFINED_LONG_VALUE,
				true);
		
		// check if the event should be purged according to
		// the purge definitions of the current ces
		EventObj currentEventObject = null;
		String eventTags = null;
		for (int i = 0; i < allEventObjects.size(); i++) {
			currentEventObject = (EventObj) allEventObjects.elementAt(i);
			eventTags = (String) currentEventObject.getField(
					EventFieldCode.TAGS_FIELD_ID).getValue();
			
			if (currentCes.shouldPurge(currentEventObject)) {
				eventTags = eventTags.substring(0,cesTagIndex) +
						"0" +
						eventTags.substring(cesTagIndex+1);

				currentEventObject.updateField(
						EventFieldCode.TAGS_FIELD_ID,
						eventTags);
				updateRecordTags(currentUser,
						currentModelDef.getTag(), 
						currentEventObject);
			}
		}
		
		// remove all obsolete events
		removeObsoleteEvents(currentUser,
				currentModelDef.getTag()); 
		
	} // purge
	

	
	
	
	/* ===========================================================
	 ==============================================================
	 =============                    =============================
	 =============      updateRecordTags =========================
	 =============                    =============================
	 ==============================================================
	 =========================================================== */
	public static boolean updateRecordTags(User currentUser, 
			String modelTag,
			EventObj currentRecord) {
		
		String eventsTableName = "event_U" +
				Utilities.getFormattedNumber(
						new Long(currentUser.getId()).intValue(), 4) +
						"_" + modelTag;

		String updateQuery = "UPDATE " + eventsTableName +
			" SET tag='" + 
			(String) currentRecord.getField(
					EventFieldCode.TAGS_FIELD_ID).getValue() + 
			"' WHERE id='" + 
			currentRecord.getId() + "';";
		try {
			DBManager.executeUpdate(updateQuery);
		}
		catch (SQLException e) {
			Log.debug(Log.ERROR, "Erro SQL(: " + e + 
					" - query = " + updateQuery, "ConfigDAO.updateRecordTags");
			return (false);
		}
		return (true);
	} // updateRecordTags
	
	

	/* ===========================================================
	 ==============================================================
	 =============                    =============================
	 =============      removeObsoleteEvents =========================
	 =============                    =============================
	 ==============================================================
	 =========================================================== */
	public static boolean removeObsoleteEvents(User currentUser, 
			String modelTag) {
		
		String eventsTableName = "event_U" +
				Utilities.getFormattedNumber(
						new Long(currentUser.getId()).intValue(), 4) +
						"_" + modelTag;

		String updateQuery = "DELETE FROM " + eventsTableName +
			" WHERE tag=0;";
		try {
			DBManager.executeUpdate(updateQuery);
		}
		catch (SQLException e) {
			Log.debug(Log.ERROR, "Erro SQL(: " + e + 
					" - query = " + updateQuery, 
					"ConfigDAO.removeObsoleteEvents");
			return (false);
		}
		return (true);
	} // removeObsoleteEvents
	
	

	public static void main(String s[]) {
//		Vector a = new Vector();
//		a.add("abc");
//		a.add(new Integer(123));
//		
//		EventObject record = new EventObject(1,a);
//		
//		insertRecord(record);
//		EventObj ce = getRecord(8);
//		ce = null;
	}
	
}; 
